﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FinanSoft.Utilities;
using FinanSoft.Entities;
using FinanSoft.BusinessLayer.BuySaleStrategy;
using FinanSoft.DataLayer;

namespace FinanSoft.BusinessLayer
{
    public class BuySaleManager
    {
        #region Fields
        private bool _IsError;
        private String _ErrorMessage;
        private static BuySaleManager _instance;
        //Encabezados de los documentos no cancelados
        private List<Document> _DocumentHeaders;
        #endregion

        #region Constructor Singleton
        /// <summary>
        /// Constructor privado de la clase para permitir la implementacion del patron singleton
        /// </summary>
        private BuySaleManager()
        {
            _IsError = false;
            _ErrorMessage = "";
            _DocumentHeaders = BuySaleDataManager.GetInstance().GetAllUncacelledDocs();
            if (_DocumentHeaders == null)
            {
                _IsError = true;
                _ErrorMessage = Constants.ERROR_UNCANCELLED_DOC_READING;
            }
            // Aqui debería de pedir los encabezados de todos los documentos
        }

        /// <summary>
        /// Metodo que obtienen la instancia existente de la clase. Si esta aun no ha sido instanciada, la instancia.
        /// </summary>
        public static BuySaleManager GetInstance()
        {
            if (_instance == null)
                _instance = new BuySaleManager();
            return _instance;
        }
        #endregion

        public string ErrorMessage
        {
            get { return _ErrorMessage; }
        }

        #region Métodos Públicos
        /// <summary>
        /// Método que se encarga de obtener los códigos únicos para los documentos
        /// que aún no han sido cancelados (órdenes y entradas de mercancías)
        /// </summary>
        /// <param name="docType">Recibe el acrónimo del tipo de documento</param>
        /// <returns>Retorna los ids de los documentos no cancelados del tipo especificado</returns>
        public List<int> GetDocUncancelled(String docType)
        {
            if (_DocumentHeaders == null)
            {
                _IsError = true;
                _ErrorMessage = Constants.ERROR_UNCANCELLED_DOC_READING;
            }
            List<int> idsDocs = new List<int>();
            foreach (Document doc in _DocumentHeaders)
            {
                if (doc.tipoDocumento == docType)
                    idsDocs.Add(doc.codigoDocumento);
            }

            if (idsDocs.Count == 0)
            {
                _IsError = true;
                _ErrorMessage = Constants.ERROR_NO_UNCANCELLED_DOCS;
            }
            return idsDocs;
        }

        /// <summary>
        /// Método que se encarga de obtener el encabezado y la lista de artículos de un documento
        /// con un código en específico y para un tipo de documento en específico
        /// </summary>
        /// <param name="uncancelledDocCode">Este es el código del documento que fue escogido como previo</param>
        /// <param name="docType">Este es el acrónimo del tipo de documeto que se escogió como previo ("OC" o "EM")</param>
        /// <returns></returns>
        public Document GetDocumentFor(int uncancelledDocCode, String docType)
        {
            if (_DocumentHeaders == null)
            {
                _IsError = true;
                _ErrorMessage = Constants.ERROR_UNCANCELLED_DOC_READING;
            }
            // llamo a la base de datos por los artículos de un documento en espe
            List<DocDetail> articles = null;
            foreach (Document doc in _DocumentHeaders)
            {
                if (doc.codigoDocumento == uncancelledDocCode && doc.tipoDocumento.Equals(docType))
                {
                    articles = DocumentManager.GetInstance().GetDocumentDetails(doc.idDocumento);
                    if (articles == null)
                    {
                        _IsError = true;
                        _ErrorMessage = Constants.ERROR_NO_ARTICLES_FOR_DOC;
                        return null;
                    }
                    doc.detalleDoc = articles.ToArray();
                    return doc;
                }
            }
            _IsError = true;
            _ErrorMessage = Constants.ERROR_NO_SUCH_DOC_FOUND;
            return null;
        }

        /// <summary>
        /// Método que se encarga de registrar un documento
        /// </summary>
        /// <param name="docType">Acrónimo del tipo de documento que és (OC, OV, EM...)</param>
        /// <param name="idAuxiliarAccount">Id de la cuenta mayor del cliente o proveedor al que está asociado</param>
        /// <param name="postingDate">Fecha de contabilización</param>
        /// <param name="deliveryExpDate">Fecha de vencimiento o de entrega</param>
        /// <param name="idMoneda">Id de la moneda en que está el documento</param>
        /// <param name="idDocBase">Id del documento base, si no tiene manda -1</param>
        /// <param name="amountBeforeTax">Monto antes de impuestos</param>
        /// <param name="taxAmount">Monto de impuestos</param>
        /// <param name="totalAmount">Monto después de impuestos</param>
        /// <returns>Retorna si hubo éxito o no con la operación</returns>
        public bool SaveDocument(String docType, int idAuxiliarAccount, DateTime postingDate, DateTime deliveryExpDate, int idMoneda, int idDocBase, double amountBeforeTax, double taxAmount, double totalAmount, List<DocDetail> details)
        {
            Document doc = new Document(docType, idAuxiliarAccount, postingDate, deliveryExpDate, idMoneda, idDocBase, amountBeforeTax, taxAmount, totalAmount);
            doc.detalleDoc = details.ToArray();
            BuySaleFactory factory = new BuySaleFactory();
            BuySaleDocManager manager = factory.Create(docType);
            bool errorDB = manager.SaveDocument(doc, docType);
            if (!errorDB)
            {
                _IsError = true;
                _ErrorMessage = manager.GetErrorMessage(); //acá hay que agarrar el mensaje de error anterior
                return false;
            }
            _DocumentHeaders = BuySaleDataManager.GetInstance().GetAllUncacelledDocs();
            return true;
        }

        /// <summary>
        /// Método que se encarga de registrar un documento
        /// </summary>
        /// <param name="docType">Acrónimo del tipo de documento que és (OC, OV, EM...)</param>
        /// <param name="idAuxiliarAccount">Id de la cuenta mayor del cliente o proveedor al que está asociado</param>
        /// <param name="postingDate">Fecha de contabilización</param>
        /// <param name="deliveryExpDate">Fecha de vencimiento o de entrega</param>
        /// <param name="idMoneda">Id de la moneda en que está el documento</param>
        /// <param name="idDocBase">Id del documento base, si no tiene manda -1</param>
        /// <param name="amountBeforeTax">Monto antes de impuestos</param>
        /// <param name="taxAmount">Monto de impuestos</param>
        /// <param name="totalAmount">Monto después de impuestos</param>
        /// <returns>Retorna si hubo éxito o no con la operación</returns>
        public bool SaveDocument(Document doc)
        {
            BuySaleFactory factory = new BuySaleFactory();
            BuySaleDocManager manager = factory.Create(doc.tipoDocumento);
            bool errorDB = manager.SaveDocument(doc, doc.tipoDocumento);
            if (!errorDB)
            {
                _IsError = true;
                _ErrorMessage = manager.GetErrorMessage(); //acá hay que agarrar el mensaje de error anterior
                return false;
            }
            _DocumentHeaders = BuySaleDataManager.GetInstance().GetAllUncacelledDocs();
            return true;
        }

        /// <summary>
        /// Método que se encarga de crear un documento para su revision previa a su registro
        /// </summary>
        /// <param name="docType">Acrónimo del tipo de documento que és (OC, OV, EM...)</param>
        /// <param name="idAuxiliarAccount">Id de la cuenta mayor del cliente o proveedor al que está asociado</param>
        /// <param name="postingDate">Fecha de contabilización</param>
        /// <param name="deliveryExpDate">Fecha de vencimiento o de entrega</param>
        /// <param name="idMoneda">Id de la moneda en que está el documento</param>
        /// <param name="idDocBase">Id del documento base, si no tiene manda -1</param>
        /// <param name="amountBeforeTax">Monto antes de impuestos</param>
        /// <param name="taxAmount">Monto de impuestos</param>
        /// <param name="totalAmount">Monto después de impuestos</param>
        /// <returns>Retorna el documento generico creado a partir de los parametros pasados</returns>
        public Document PreviewDocument(String docType, int idAuxiliarAccount, DateTime postingDate, DateTime deliveryExpDate, int idMoneda, int idDocBase, double amountBeforeTax, double taxAmount, double totalAmount, List<DocDetail> details)
        {
            Document doc = new Document(docType, idAuxiliarAccount, postingDate, deliveryExpDate, idMoneda, idDocBase, amountBeforeTax, taxAmount, totalAmount);
            doc.detalleDoc = details.ToArray();

            return doc; 
        }

        /// <summary>
        /// Esta sobrecarga es para poder guardar facturas de cliente y notas de crédito que sean especificamente de servicios
        /// </summary>
        /// <param name="docType"></param>
        /// <param name="idAuxiliarAccount"></param>
        /// <param name="postingDate"></param>
        /// <param name="deliveryExpDate"></param>
        /// <param name="idMoneda"></param>
        /// <param name="amountBeforeTax"></param>
        /// <param name="taxAmount"></param>
        /// <param name="totalAmount"></param>
        /// <param name="accountId"></param>
        /// <param name="usedTax"></param>
        /// <param name="description"></param>
        /// <returns></returns>
        public bool SaveDocument(String docType, int idAuxiliarAccount, DateTime postingDate, DateTime deliveryExpDate, int idMoneda, double amountBeforeTax, double taxAmount, double totalAmount, int accountId, Tax usedTax, string description)
        {
            Document doc = new Document(docType, idAuxiliarAccount, postingDate, deliveryExpDate, idMoneda, Constants.INVALID_ID, amountBeforeTax, taxAmount, totalAmount);
            ServiceDocManager manager = new ServiceDocManager();
            bool errorDB = manager.SaveDocument(doc, CatalogManager.GetInstance().GetAccount(accountId), docType, usedTax, description);
            if (!errorDB)
            {
                _IsError = true;
                _ErrorMessage = manager.GetErrorMessage();
                return false;
            }
            return true;
        }

        public Document GetDocument(int idDoc)
        {
            foreach (Document doc in _DocumentHeaders)
                if (doc.idDocumento == idDoc) return doc;
            return null;
        }

        /// <summary>
        /// Método que se encarga de actualizar el id del último documento insertado
        /// para las facturas externas
        /// </summary>
        /// <param name="documentId">Id real</param>
        /// <returns>Retorna si la operación tuvo o no éxito</returns>
        public bool UpdateDocumentId(int documentId)
        {
            bool noErrorDB = BuySaleDataManager.GetInstance().UpdateDocumentId(documentId);
            if (!noErrorDB)
            {
                _IsError = true;
                _ErrorMessage = Constants.ERROR_UPDATING_DOC_ID;
                return false;
            }
            return true;
        }
        #endregion

        #region Métodos privados
        /// <summary>
        /// Método que se encarga de devolver el tipo de documento para un documento de los no cancelados que
        /// se pueden utilizar como documento base para uno nuevo
        /// </summary>
        /// <param name="docBaseId">Id del documento base a consultar</param>
        /// <returns>Retorna el acrónimo, el tipo de documento</returns>
        public String GetDocBaseType(int docBaseId)
        {
            foreach (Document doc in _DocumentHeaders)
            {
                if (doc.idDocumento == docBaseId)
                    return doc.tipoDocumento;
            }
            return "";
        }
        #endregion
    }
}
